#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "raylib.h"
#include "pub_lib.h"
#include "FreeImage.h"
#include "image.h"
#include "dpi_algo.h"

const int screenWidth = 1024;
const int screenHeight = 768;

cpu_image effect_data;
run_state gstate = STATE_IDLE;

char* hint_right = NULL;
char hint_left[128] = "image size";

#define MAX_CMD_NUM 8
const char command_str_en[MAX_CMD_NUM][MAX_HINT_STRING_LEN] =
{
	//chapter 9.6.1
	"1. Erosion and Dilation",//example 9.9

	//chapter 9.6.2
	"2. Opening and Closing", //example 9.10

	//chapter 9.6.3
	"3. smoothing", //figure 9.38
	"4. gradient", //figure 9.39
	"5. Top-hat transfrom", //figure 9.40
	"6. Granulometry", //figure 9.41
	"7. Textural segmentation", //figure 9.43

	//chapter 9.6.4
	"8. Reconstruction" //example 9.11
};

void init_effect_data(void)
{
	memset(&effect_data, 0, sizeof(cpu_image));
	effect_data.win_width = screenWidth;
	effect_data.win_height = screenHeight;
	effect_data.fb_pixels = effect_data.win_width * effect_data.win_height;
	effect_data.startx = 10;
	effect_data.starty = 30;
	effect_data.left_max = get_max_pixel(screenWidth, 0.3f);
	effect_data.right_max = get_max_pixel(screenWidth, 0.7f);
	effect_data.right_image_max = effect_data.right_max;;

	effect_data.split_line = effect_data.startx + effect_data.left_max + 5;
	effect_data.lx = effect_data.startx;
	effect_data.rx = effect_data.split_line + 5;
}

void init_fb(void)
{
	effect_data.frame_buf = calloc(effect_data.fb_pixels, sizeof(Color));
}

static BYTE* gresult = NULL;
static void extra_init(void)
{
	int w, h;
	w = effect_data.image_width;
	h = effect_data.image_height;
	if(!gresult)
		gresult = malloc(w * h);
}

static void extra_destroy(void)
{
	if(gresult)
		free(gresult);
	gresult = NULL;
}

/*
 * 打开图片，读入图片数据
 * 原始图片信息:  ori
 * 左边显示的小图片: left
 * 右边显示的大图片: right
 * 右边显示的大图片的更改后的数据: rchg
 * 右边显示的大图片，中间计算的结果：rchg_data
 * */
int init_effect(char* path, int right_window_num)
{
	effect_data.right_window_num = right_window_num;
	const char* ext = GetFileExtension(path);

	FIBITMAP* ori;
	ori = open_img(path);
	if (!ori)
		return -1;
	if (!strcmp(ext, ".tif")) {
		read_img(ori, &effect_data);
	}

	int size = effect_data.image_width * effect_data.image_height;
	effect_data.ori_data = malloc(size);
	effect_data.chg_data = malloc(size);
	init_image_datab(ori, effect_data.ori_data);
	effect_data.chg = copy_img(ori);

	effect_data.show_histo = 0;
	effect_data.order = 0;
	effect_data.ori = (void*)ori;
	effect_data.left = (void*)ori;
	effect_data.right = (void*)ori;
	//check if need scale of left image
	int max = effect_data.left_max; //最长或最宽不超过360
	int neww = max, newh = max;
		if ( effect_data.image_width > max || effect_data.image_height > max) {
		cal_scaled_val(max, effect_data.image_width, effect_data.image_height,\
				&neww, &newh);
		effect_data.left = FreeImage_Rescale(ori, neww, newh, FILTER_BILINEAR);
	}

	//check if need scale of right image
	if(right_window_num == 1)
		effect_data.right_image_max = get_max_pixel(screenWidth, 0.7f);
	else
		effect_data.right_image_max = split_right_window(effect_data.win_width, 0.7f);

	max = effect_data.right_image_max;
	neww = effect_data.image_width;
	newh = effect_data.image_height;
	if (effect_data.image_width > max || effect_data.image_height > max) {
		cal_scaled_val(max, effect_data.image_width, effect_data.image_height, \
			&neww, &newh);
		effect_data.right = FreeImage_Rescale(ori, neww, newh, FILTER_BILINEAR);
	}

	sprintf(hint_left, "ori:%dx%d  chg:%dx%d",
		effect_data.image_width, effect_data.image_height, neww, newh);

	effect_data.rchg = FreeImage_Copy(effect_data.right, 0, 0, neww, newh);
	effect_data.pri  = FreeImage_Copy(effect_data.right, 0, 0, neww, newh);

	//int data[rw][rh]
	effect_data.rw = neww;
	effect_data.rh = newh;
	effect_data.rchg_data = calloc(neww * newh, sizeof(float));
	effect_data.rchg_image = calloc(neww * newh, 1);
	extra_init();

	effect_data.image_size = GetPixelDataSize(effect_data.image_width,\
		effect_data.image_height, effect_data.format);
	effect_data.pix_len = get_pixel_bytes(effect_data.image_width,\
		effect_data.image_height, effect_data.image_size);

	return 0;
}

void unload_image(void)
{
	if(effect_data.left != effect_data.ori)
		close_img(effect_data.left);
	if(effect_data.right != effect_data.ori)
		close_img(effect_data.right);
	if(effect_data.ori)
		close_img(effect_data.ori);
	if(effect_data.chg)
		close_img(effect_data.chg);
	if(effect_data.rchg)
		close_img(effect_data.rchg);
	if(effect_data.pri)
		close_img(effect_data.pri);

	if(effect_data.ori_data)
		free(effect_data.ori_data);
	if(effect_data.chg_data)
		free(effect_data.chg_data);

	if(effect_data.rchg_data)
		free(effect_data.rchg_data);
	if(effect_data.rchg_image)
		free(effect_data.rchg_image);
	extra_destroy();
}

void destroy_effect(void)
{
	free(effect_data.frame_buf);
	unload_image();
}

void switch_next_image(char* filename, int right_window_num)
{
	int ret;
	char path[256];

	clear_fb(&effect_data, RAYWHITE);
	unload_image();
	get_full_path(IMAGE_PATH, filename, path);
	ret = init_effect(path, right_window_num);
	if (ret)
		return;
	show_image(effect_data.left, &effect_data,\
		effect_data.startx, effect_data.starty);
}

void handle_key_down(void)
{
	
}

void handle_key_up(void)
{
}

void morph_recon_gray_show(void)
{
	int w, h;
	w = effect_data.image_width;
	h = effect_data.image_height;

	if(effect_data.order == 0) {
		effect_data.order = 1;
		gstate = STATE_WAIT_NEXT;
		hint_right = "suppressing horizontal reflection, press space key";

		//open recon
		erosion_gray_hor(effect_data.ori_data, effect_data.chg_data, 71);
		morph_recon_dgray(effect_data.chg_data, effect_data.ori_data, gresult);
		show_scaled_datas(gresult, 1);

		mem_subc(effect_data.ori_data, gresult, gresult);
		show_scaled_datas(gresult, 3);

		//open
		morph_open_common_gray(effect_data.ori_data, effect_data.chg_data,
			w, h, SE_TYPE_HOR, 71);
		show_scaled_datas(effect_data.chg_data, 2);
		//ori - open
		mem_subc(effect_data.ori_data, effect_data.chg_data, effect_data.chg_data);
		show_scaled_datas(effect_data.chg_data, 4);
	} else {
		effect_data.order = 2;
		gstate = STATE_FINISH;
		hint_right = "remove vertical reflection";

		//used figure3, gresult, figure 9.44(d)
		//open recon, figure f
		erosion_gray_hor(gresult, effect_data.chg_data, 11);
		morph_recon_dgray(effect_data.chg_data, gresult, effect_data.ori_data);
		show_scaled_datas(effect_data.ori_data, 1);

		//dilation, figure g
		dilation_gray_hor(effect_data.ori_data, effect_data.chg_data, 21);
		show_scaled_datas(effect_data.chg_data, 2);

		//gresult = min(figure g, figure d), figure h
		image_get_min(gresult, effect_data.chg_data, effect_data.chg_data, w * h);
		show_scaled_datas(effect_data.chg_data, 3);

		//marker = figure h = effect_data.chg_data
		//mask = figure d = gresult
		morph_recon_dgray(effect_data.chg_data, gresult, effect_data.ori_data);
		show_scaled_datas(effect_data.ori_data, 4);
	}
}

void handle_key_space(void)
{
	if(gstate != STATE_WAIT_NEXT)
		return;

	switch(effect_data.cmd) {
		case 8:
			morph_recon_gray_show();
			break;
		default:
			break;
	}
}

void special_effect(int cmd)
{
	//repeate press same key
	if (effect_data.cmd == cmd)
		return;

	if(cmd <= MAX_CMD_NUM) {
		effect_data.cmd = cmd;
		hint_right = (char*)command_str_en[cmd - 1];
		SetWindowTitle(command_str_en[cmd - 1]);
	}

	int w, h;
	switch (cmd) {
	case 1:
		switch_next_image("Fig0935(a)(ckt_board_section).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;
		show_scaled_datas(effect_data.ori_data, 1);

		erosion_mem_gray(effect_data.ori_data, effect_data.chg_data, 2);
		show_scaled_datas(effect_data.chg_data, 2);

		dilation_mem_gray(effect_data.ori_data, effect_data.chg_data, 2);
		show_scaled_datas(effect_data.chg_data, 3);

		morph_gradient(effect_data.ori_data, effect_data.chg_data, w, h, 2);
		show_scaled_datas(effect_data.chg_data, 4);
		break;
	case 2:
		switch_next_image("Fig0935(a)(ckt_board_section).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;
		show_scaled_datas(effect_data.ori_data, 1);

		morph_open_gray(effect_data.ori_data, effect_data.chg_data, 3);
		show_scaled_datas(effect_data.chg_data, 2);

		morph_close_gray(effect_data.ori_data, effect_data.chg_data, 5);
		show_scaled_datas(effect_data.chg_data, 3);
		break;
	case 3:
		switch_next_image("Fig0938(a)(cygnusloop_Xray_original).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;
		show_scaled_datas(effect_data.ori_data, 1);

		morph_smooth_gray(effect_data.ori_data, effect_data.chg_data, w, h, 1);
		show_scaled_datas(effect_data.chg_data, 2);

		morph_smooth_gray(effect_data.ori_data, effect_data.chg_data, w, h, 3);
		show_scaled_datas(effect_data.chg_data, 3);

		morph_smooth_gray(effect_data.ori_data, effect_data.chg_data, w, h, 5);
		show_scaled_datas(effect_data.chg_data, 4);
		break;
	case 4:
		switch_next_image("Fig0939(a)(headCT-Vandy).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;
		show_scaled_datas(effect_data.ori_data, 1);

		int r = 3;
		dilation_mem_gray_common(effect_data.ori_data, effect_data.chg_data,
			w, h, SE_TYPE_SQUARE, r);
		show_scaled_datas(effect_data.chg_data, 2);

		erosion_mem_gray_common(effect_data.ori_data, effect_data.chg_data,
			w, h, SE_TYPE_SQUARE, r);
		show_scaled_datas(effect_data.chg_data, 3);

		morph_gradient(effect_data.ori_data, effect_data.chg_data, w, h, r);
		show_scaled_datas(effect_data.chg_data, 4);
		break;
	case 5:
		switch_next_image("Fig0940(a)(rice_image_with_intensity_gradient).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;
		morph_open_gray(effect_data.ori_data, effect_data.chg_data, 40);
		show_scaled_datas(effect_data.chg_data, 1);

		mem_add(effect_data.ori_data, effect_data.chg_data,
			effect_data.chg_data, -1, w, h);
		show_scaled_datas(effect_data.chg_data, 2);
		break;
	case 6:
		switch_next_image("Fig0941(a)(wood_dowels).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;

		morph_smooth_gray(effect_data.ori_data, effect_data.chg_data, w, h, 5);
		show_scaled_datas(effect_data.chg_data, 1);

		morph_open_gray(effect_data.ori_data, effect_data.chg_data, 10);
		show_scaled_datas(effect_data.chg_data, 2);

		morph_open_gray(effect_data.ori_data, effect_data.chg_data, 25);
		show_scaled_datas(effect_data.chg_data, 3);

		morph_open_gray(effect_data.ori_data, effect_data.chg_data, 30);
		show_scaled_datas(effect_data.chg_data, 4);
		break;
	case 7:
		switch_next_image("Fig0943(a)(dark_blobs_on_light_background).tif", 4);
		w = effect_data.image_width;
		h = effect_data.image_height;
		show_scaled_datas(effect_data.ori_data, 1);

		morph_close_gray(effect_data.ori_data, gresult, 31);
		show_scaled_datas(gresult, 2);

		morph_open_gray(gresult, effect_data.chg_data, 60);
		show_scaled_datas(effect_data.chg_data, 3);

		morph_gradient(effect_data.chg_data, gresult, w, h, 3);
		mem_addc(effect_data.ori_data, gresult, effect_data.chg_data);
		show_scaled_datas(effect_data.chg_data, 4);
		break;
	case 8:
		switch_next_image("Fig0944(a)(calculator).tif", 4);
		morph_recon_gray_show();
		break;
	case KEY_DOWN:
		handle_key_down();
		break;
	case KEY_UP:
		handle_key_up();
		break;
	case KEY_SPACE:
		handle_key_space();
		break;
	default:
		hint_right = "not support command";
		break;
	}//end switch
}

static void draw_titles(void)
{
	switch(effect_data.cmd) {
		case 1:
			draw_title("original, 9.35a", 1);
			draw_title("erosion, 9.35b", 2);
			draw_title("dilation, 9.35c", 3);
			break;
		case 2:
			draw_title("original, 9.37a", 1);
			draw_title("open r=3, 9.37b", 2);
			draw_title("close r=5, 9.37c", 3);
			break;
		case 3:
			draw_title("original, 9.38a", 1);
			draw_title("smooth r=1, 9.38b", 2);
			draw_title("smooth r=3, 9.38c", 3);
			draw_title("smooth r=5, 9.38d", 4);
			break;
		case 4:
			draw_title("original, 9.39a", 1);
			draw_title("dilation, 9.39b", 2);
			draw_title("erosion, 9.39c", 3);
			draw_title("gradient, 9.39d", 4);
			break;
		case 5:
			draw_title("r=40, 9.40c", 1);
			draw_title("top-hat result, 9.40d", 2);
			break;
		case 6:
			draw_title("smoothing, 9.41b", 1);
			draw_title("open r=10, 9.41c", 2);
			draw_title("open r=25, 9.41e", 3);
			draw_title("open r=30, 9.41f", 4);
			break;
		case 7:
			draw_title("original, 9.43a", 1);
			draw_title("close, 9.43b", 2);
			draw_title("open with b, 9.43c", 3);
			draw_title("9.43d", 4);
			break;
		case 8:
			if(effect_data.order == 1) {
				draw_title("reconOpen, 9.44b", 1);
				draw_title("open, 9.44c", 2);
				draw_title("ori - reconOpen, 9.44d", 3);
				draw_title("ori - open, 9.44e", 4);
			} else {
			}
			break;
		default:
			break;
	}
}

int main(int argc, char** argv)
{
	// Initialization
	//--------------------------------------------------------------------------------------
	FreeImage_Initialise(TRUE);
	init_effect_data();
	init_fb();

	InitWindow(screenWidth, screenHeight, "demo");
	// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
	SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
	//--------------------------------------------------------------------------------------

	int key = 0;
	int change = 0;
	Vector2 mouse = GetMousePosition();
	hint_right = "press number key";

	// Main game loop
	while (!WindowShouldClose())    // Detect window close button or ESC key
	{
		// Update
		key = GetKeyPressed();
		if (key >= '1' && key <= '9') {
			change = 1;
			key = key - '0';
		} else if(key == KEY_DOWN || key == KEY_UP){
			change = 1;
		}
		else if(key == KEY_SPACE) {
			change = 1;
		}
		else if (key == ESC_KEY) {
			break;
		}

		if (change) {
			change = 0;
			special_effect(key);
		}
		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------

		// Draw
		//----------------------------------------------------------------------------------
		BeginDrawing();
		ClearBackground(RAYWHITE);

		DrawText(hint_left, effect_data.startx, 5, 20, BLACK);
		if (hint_right) {
			DrawText(hint_right, effect_data.rx, 0, 20, BLACK);
		}
		show_info_en(command_str_en, MAX_CMD_NUM, effect_data.startx,\
			effect_data.left_max + effect_data.starty + 20);
		draw_image(effect_data.frame_buf, effect_data.win_width, effect_data.win_height);
		DrawLine(effect_data.split_line, effect_data.starty,\
			effect_data.split_line, GetScreenHeight(), GRAY);
		draw_titles();

		mouse = GetMousePosition();
		DrawText(TextFormat("mouse(%d, %d)", (int)mouse.x, (int)mouse.y), \
			GetScreenWidth() - 200, 10, 20, BLACK);

		EndDrawing();
		//----------------------------------------------------------------------------------
	}

	// De-Initialization
	destroy_effect();
	FreeImage_DeInitialise();

	//--------------------------------------------------------------------------------------
	CloseWindow();        // Close window and OpenGL context
	//--------------------------------------------------------------------------------------

	return 0;
}


